package org.sunspotworld.demo;

import java.util.Random;

/**
 * A class to encapsulate a matrix of probabilities used to pick random
 * music features such as pitch and rhythm.
 */
public class DecisionMatrix
{
    protected float[][] m_roleMatrix;
    protected float[][] m_modMatrix;
    protected int m_size;
    private static final int size_Min = 2;
    protected float[][] m_lowestValence;
    protected float[][] m_highestValence;
    protected float[][] m_lowestArousal;
    protected float[][] m_highestArousal;
    protected float[][][] m_cords;
    private Random m_random;

    // CONSTRUCTORS
    public DecisionMatrix(int size)
    {
        if(size < size_Min)
        {
            m_size = size_Min;
        }
        else
        {
            m_size = size;
        }
        m_modMatrix = new float[m_size][m_size];
        m_roleMatrix = new float[m_size][m_size];
    
        m_random = new Random();
    }

    
    /*public DecisionMatrix(float[][] m)
    {
        m_size = m[0].length;
        m_roleMatrix = m;
        
        m_modMatrix = new double[m_size][m_size];
        m_modMatrix = new float[m_size][m_size];
        for(int i = 0; i < m_size; i++)
        {
            for(int j = 0; j < m_size; j++)
            {
                m_modMatrix[i][j] = 1;
            }
        }
        fixAllRows();
    }
    }*/

    // METHODS
    /*public float[][] getMatrix()
    {
        return m_roleMatrix;
    }
    }*/
    
    public int getSize()
    {
        return m_size;
    }
    
    
    /**
     * Make sure a row (with modifiers applied) sums up to 100% (1.00). When fixing,
     * the function only changes the modifiers, not the role matrix.
     * 
     * @param rowIndex
     */
    private void fixRow(int rowIndex)
    {
        float sum = 0f;
        for(int i = 0; i < m_size; i++)
        {
            sum += m_roleMatrix[rowIndex][i] * m_modMatrix[rowIndex][i];
        }
        for(int i = 0; i < m_size; i++)
        {
            m_modMatrix[rowIndex][i] /= sum;
        }
    }
    
    
    /**
     * Make sure all rows sum up correctly.
     */
    protected void fixAllRows()
    {
        for(int i = 0; i < m_size; i++)
        {
            fixRow(i);
        }
    }
    
    
    /**
     * Pick a random column index depending on the weights in the given row.
     * 
     * @param rowIndex
     * @return random column, depending on weights
     */
    public int getRandomIndex(int rowIndex)
    {
        updateRowModifiers(rowIndex);
        
        float rand = m_random.nextFloat();
        float sum = 0f;
        int i;
        
        for(i = 0; i < m_size; i++)
        {
            sum += m_roleMatrix[rowIndex][i] * m_modMatrix[rowIndex][i];
            if(sum > rand)
            {
                break;
            }
        }
        
        return i;
    }
    
    
    /**
     * Update the indicated row to reflect the latest AV, by averaging the low
     * and high valence and arousal tables. This lets us change
     * limited parts of the modifiersMatrix at a time, to reduce overhead.
     * 
     * @param av the valence and arousal to weight by
     * @param rowIndex
     */
    private void updateRowModifiers(int rowIndex)
    {
        //Weight the local and global AV to figure out an average
        float averageValence = (PlanningThread.localAV.getValence() + AV.GLOBAL_FACTOR *
                PlanningThread.globalAV.getValence()) / (1 + AV.GLOBAL_FACTOR);
        float averageArousal = (PlanningThread.localAV.getArousal() + AV.GLOBAL_FACTOR *
                PlanningThread.globalAV.getArousal()) / (1 + AV.GLOBAL_FACTOR);
        
        float valenceSlope;
        float valenceMod;
        float arousalSlope;
        float arousalMod;
            
        for(int i = 0; i < m_size; i++)
        {
            valenceSlope = m_highestValence[rowIndex][i] - m_lowestValence[rowIndex][i];
            valenceMod = m_lowestValence[rowIndex][i] + averageValence * valenceSlope;
            
            arousalSlope = m_highestArousal[rowIndex][i] - m_lowestArousal[rowIndex][i];
            arousalMod = m_lowestArousal[rowIndex][i] + averageArousal * arousalSlope;
            
            m_modMatrix[rowIndex][i] = valenceMod * arousalMod;
            
            if(valenceMod < 0 || arousalMod < 0)
            {
                System.out.println("Negative matrix weights. A: " + arousalMod + ", V: " + valenceMod);
            }
        }
        
        fixRow(rowIndex);
    }
}
